<html>

<head>
	<meta charset="utf-8">
	<title>VSCode Tests</title>
	<link href="../../../node_modules/mocha/mocha.css" rel="stylesheet" />
</head>

<body>
	<div id="mocha"></div>
	<script src="../../../node_modules/mocha/mocha.js"></script>
	<script>
		// !!! DO NOT CHANGE !!!
		// Our unit tests may run in environments without
		// display (e.g. from builds) and tests may by
		// accident bring up native dialogs or even open
		// windows. This we cannot allow as it may crash
		// the test run.
		// !!! DO NOT CHANGE !!!
		window.open = function () { throw new Error('window.open() is not supported in tests!'); };
		window.alert = function () { throw new Error('window.alert() is not supported in tests!'); }
		window.confirm = function () { throw new Error('window.confirm() is not supported in tests!'); }

		// Ignore uncaught cancelled promise errors
		window.addEventListener('unhandledrejection', e => {
			const name = e && e.reason && e.reason.name;

			if (name === 'Canceled') {
				e.preventDefault();
				e.stopPropagation();
			}
		});

		const urlParams = new URLSearchParams(window.location.search);
		const isCI = urlParams.get('ci');

		const $globalThis = globalThis;
		const setTimeout0IsFaster = (typeof $globalThis.postMessage === 'function' && !$globalThis.importScripts);

		/**
		 * See https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#:~:text=than%204%2C%20then-,set%20timeout%20to%204,-.
		 *
		 * Works similarly to `setTimeout(0)` but doesn't suffer from the 4ms artificial delay
		 * that browsers set when the nesting level is > 5.
		 */
		const setTimeout0 = (() => {
			if (setTimeout0IsFaster) {
				const pending = [];

				$globalThis.addEventListener('message', (e) => {
					if (e.data && e.data.vscodeScheduleAsyncWork) {
						for (let i = 0, len = pending.length; i < len; i++) {
							const candidate = pending[i];
							if (candidate.id === e.data.vscodeScheduleAsyncWork) {
								pending.splice(i, 1);
								candidate.callback();
								return;
							}
						}
					}
				});
				let lastId = 0;
				return (callback) => {
					const myId = ++lastId;
					pending.push({
						id: myId,
						callback: callback
					});
					$globalThis.postMessage({ vscodeScheduleAsyncWork: myId }, '*');
				};
			}
			return (callback) => setTimeout(callback);
		})();

		Mocha.Runner.immediately = setTimeout0;

		mocha.setup({
			ui: 'tdd',
			timeout: isCI ? 30000 : 5000
		});
	</script>

	<script>
		const isBuild = urlParams.get('build');
		const out = !!isBuild ? 'out-build' : 'out';
		const tasks =[];

		// configure loader
		const baseUrl = window.location.href;

		// generate import map
		const importMapParent = document.currentScript.parentNode;
		const importMap = {
			imports: {
				vs: new URL(`../../../${out}/vs`, baseUrl).href,
				assert: new URL('../assert.js', baseUrl).href,
				sinon: new URL('../../../node_modules/sinon/pkg/sinon-esm.js', baseUrl).href,
				'sinon-test': new URL('../../../node_modules/sinon-test/dist/sinon-test-es.js', baseUrl).href,
				'@xterm/xterm': new URL('../../../node_modules/@xterm/xterm/lib/xterm.js', baseUrl).href,
				'@vscode/iconv-lite-umd': new URL('../../../node_modules/@vscode/iconv-lite-umd/lib/iconv-lite-umd.js', baseUrl).href,
				jschardet: new URL('../../../node_modules/jschardet/dist/jschardet.min.js', baseUrl).href
			}
		}

		// ---- CSS tricks

		const cssDataBase64 = urlParams.get('_devCssData');
		if (typeof cssDataBase64 === 'string') {

			const style = document.createElement('style');
			style.type = 'text/css';
			document.head.appendChild(style);

			globalThis._VSCODE_CSS_LOAD = function (url) {
				// debugger;
				style.sheet.insertRule(`@import url(${url});`);
			};

			const cssData = Uint8Array.from(atob(cssDataBase64), c => c.charCodeAt(0));
			tasks.push(new Response(new Blob([cssData], {type:'application/octet-binary'}).stream().pipeThrough(new DecompressionStream('gzip'))).text().then(value => {
				const cssModules = value.split(',');
				for (const cssModule of cssModules) {
					const cssUrl = new URL(`../../../${out}/${cssModule}`, baseUrl).href;
					const jsSrc = `globalThis._VSCODE_CSS_LOAD('${cssUrl}');\n`;
					const blob = new Blob([jsSrc], { type: 'application/javascript' });
					importMap.imports[cssUrl] = URL.createObjectURL(blob);
				}
			}).catch(err => {
				console.error(err);
			}));
		}

		const initPromise = Promise.allSettled(tasks).then(() => {

			const rawImportMap = JSON.stringify(importMap, undefined, 2);
			const importMapScript = document.createElement('script');
			importMapScript.type = 'importmap';
			importMapScript.textContent = rawImportMap;
			importMapParent.appendChild(importMapScript);

		}).then(() => {
			const bootstrapScript = document.createElement('script');
			bootstrapScript.type = 'module';
			bootstrapScript.textContent = document.getElementById('bootstrap').textContent
			document.getElementById('bootstrap').remove();
			document.body.append(bootstrapScript);
		});

		// set up require

		globalThis._VSCODE_FILE_ROOT = new URL('../../../src', baseUrl).href;
		globalThis.require = {
			paths: {
				xterm: new URL('../../../node_modules/xterm', baseUrl).href,
				'@vscode/iconv-lite-umd': new URL('../../../node_modules/@vscode/iconv-lite-umd', baseUrl).href,
				jschardet: new URL('../../../node_modules/jschardet', baseUrl).href
			}
		}
	</script>

	<script>
		function serializeSuite(suite) {
			return {
				root: suite.root,
				suites: suite.suites.map(serializeSuite),
				tests: suite.tests.map(serializeRunnable),
				title: suite.title,
				fullTitle: suite.fullTitle(),
				titlePath: suite.titlePath(),
				timeout: suite.timeout(),
				retries: suite.retries(),
				slow: suite.slow(),
				bail: suite.bail()
			};
		}
		function serializeRunnable(runnable) {
			return {
				title: runnable.title,
				titlePath: runnable.titlePath(),
				fullTitle: runnable.fullTitle(),
				async: runnable.async,
				slow: runnable.slow(),
				speed: runnable.speed,
				duration: runnable.duration,
				currentRetry: runnable.currentRetry(),
			};
		}
		function serializeError(err) {
			return {
				message: err.message,
				stack: err.stack,
				actual: err.actual,
				expected: err.expected,
				uncaught: err.uncaught,
				showDiff: err.showDiff,
				inspect: typeof err.inspect === 'function' ? err.inspect() : ''
			};
		}
		function PlaywrightReporter(runner) {
			runner.on('start', () => window.mocha_report('start'));
			runner.on('end', () => window.mocha_report('end'));
			runner.on('suite', suite => window.mocha_report('suite', serializeSuite(suite)));
			runner.on('suite end', suite => window.mocha_report('suite end', serializeSuite(suite)));
			runner.on('test', test => window.mocha_report('test', serializeRunnable(test)));
			runner.on('test end', test => window.mocha_report('test end', serializeRunnable(test)));
			runner.on('hook', hook => window.mocha_report('hook', serializeRunnable(hook)));
			runner.on('hook end', hook => window.mocha_report('hook end', serializeRunnable(hook)));
			runner.on('pass', test => window.mocha_report('pass', serializeRunnable(test)));
			runner.on('fail', (test, err) => window.mocha_report('fail', serializeRunnable(test), serializeError(err)));
			runner.on('pending', test => window.mocha_report('pending', serializeRunnable(test)));
		};

		const remoteMethods = [
			'__readFileInTests',
			'__writeFileInTests',
			'__readDirInTests',
			'__unlinkInTests',
			'__mkdirPInTests',
		];

		for (const method of remoteMethods) {
			const prefix = window.location.pathname.split('/')[1];
			globalThis[method] = async (...args) => {
				const res = await fetch(`/${prefix}/remoteMethod/${method}`, {
					body: JSON.stringify(args),
					method: 'POST',
					headers: { 'Content-Type': 'application/json' }
				});

				return res.json();
			}
		}

		async function loadModules(modules) {
			for (const file of modules) {
				mocha.suite.emit(Mocha.Suite.constants.EVENT_FILE_PRE_REQUIRE, globalThis, file, mocha);
				const m = await new Promise((resolve, reject) => import(`../../../${out}/${file}.js`).then(resolve, err => {
					console.log("BAD " + file + JSON.stringify(err, undefined, '\t'));
					resolve({});
				}));
				mocha.suite.emit(Mocha.Suite.constants.EVENT_FILE_REQUIRE, m, file, mocha);
				mocha.suite.emit(Mocha.Suite.constants.EVENT_FILE_POST_REQUIRE, globalThis, file, mocha);
			}
		}

		let _resolveTestData;
		let _resolveTestRun;
		globalThis._VSCODE_TEST_RUN = new Promise(resolve => _resolveTestData = resolve)

		window.loadAndRun = async function loadAndRun(data, manual = false) {
			_resolveTestData({data, manual})
			return new Promise(resolve => _resolveTestRun = resolve);
		}

		const modules = new URL(window.location.href).searchParams.getAll('m');
		if (Array.isArray(modules) && modules.length > 0) {
			console.log('MANUALLY running tests', modules);

			loadAndRun(modules, true).then(() => console.log('done'), err => console.log(err));
		}
	</script>

	<script type="text" id="bootstrap">
		const {data: {modules, grep}, manual} = await globalThis._VSCODE_TEST_RUN

		// load
		await loadModules(modules);

		// run
		await new Promise((resolve, reject) => {
			if (grep) {
				mocha.grep(grep);
			}

			if (!manual) {
				mocha.reporter(PlaywrightReporter);
			}
			mocha.run(failCount => resolve(failCount === 0));
		});

		_resolveTestRun();
	</script>
</body>

</html>
